package com.example.springdemo.utils.sftp;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

/**
 * @author zhangding
 * @description ftp连接管理
 * @create 2021-09-14 10:51
 **/
@Slf4j
public class SftpConnection {
    private static final String FTP_PROTOCOL="sftp";
    private SftpProperties sftpProperties;
    // 设置第一次登陆的时候提示，可选值：(ask | yes | no)
    private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";

    private static final String FILE_TYPE_NAVIGATE_HOME_DIR = ".";
    private static final String FILE_TYPE_NAVIGATE_PRE_DIR = "..";

    private ChannelSftp channelSftp ;

    public ChannelSftp getChannelSftp() {
        return channelSftp;
    }

    public SftpConnection(SftpProperties sftpProperties){
       this.sftpProperties = sftpProperties;
        try {
            this.channelSftp = createSftpChannel();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建SFTP连接
     * @return
     * @throws Exception
     */
    private ChannelSftp createSftpChannel() throws Exception {
        JSch jsch = new JSch();
        log.info("Try to connect sftp[" + sftpProperties.getUsername() + "@" + sftpProperties.getHost() + "], use password[" + sftpProperties.getPassword() + "]");

        Session session = createSession(jsch, sftpProperties.getHost(), sftpProperties.getUsername(), sftpProperties.getPort());
        session.setPassword(sftpProperties.getPassword());
        session.connect(sftpProperties.getSessionConnectTimeout());

        log.info("Session connected to {}.", sftpProperties.getHost());

        Channel channel = session.openChannel(FTP_PROTOCOL);
        channel.connect(sftpProperties.getChannelConnectedTimeout());

        log.info("Channel created to {}.", sftpProperties.getHost());

        return (ChannelSftp) channel;
    }


    /**
     * 创建会话
     * @param jsch
     * @param host
     * @param username
     * @param port
     * @return
     * @throws Exception
     */
    private Session createSession(JSch jsch, String host, String username, Integer port) throws Exception {
        Session session = null;

        if (port <= 0) {
            session = jsch.getSession(username, host);
        } else {
            session = jsch.getSession(username, host, port);
        }

        if (session == null) {
            throw new Exception(host + " session is null");
        }
        session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, sftpProperties.getSessionStrictHostKeyChecking());
        return session;
    }
    /**
     * 关闭连接
     * @param sftp
     */
    private void disconnect(ChannelSftp sftp) {
        try {
            if (sftp != null) {
                if (sftp.isConnected()) {
                    sftp.disconnect();
                } else if (sftp.isClosed()) {
                    log.info("sftp is closed already");
                }
                if (null != sftp.getSession()) {
                    sftp.getSession().disconnect();
                }
            }
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }

    public void disconnect(){
        disconnect(this.channelSftp);
    }


    /**
     * 下载文件
     * @param targetPath
     * @return
     * @throws Exception
     */
    public void downloadFile(String sourcePath,String targetPath) throws Exception {
        OutputStream outputStream = null;
        try {
            String ftpSourcePath = sftpProperties.getRoot()+sourcePath;
            channelSftp.cd(ftpSourcePath);
            log.info("Change path to {}", ftpSourcePath);

            List<String> fileList = listFiles(sourcePath);
            for(String fileName:fileList){
                File dir = new File(targetPath);
                if(!dir.exists()){
                    dir.mkdirs();
                }
                File file = new File(targetPath+fileName);
                outputStream = new FileOutputStream(file);
                channelSftp.get(fileName, outputStream);
            }
            log.info("Download file success. TargetPath: {}", targetPath);
        } catch (Exception e) {
            log.error("Download file failure. TargetPath: {}", targetPath, e);
            throw new Exception("Download File failure");
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }



    /**
     * 指定目录下文件列表
     * @param path
     * @return
     * @throws SftpException
     */
    public List<String> listFiles(String path) throws SftpException {
        Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(this.sftpProperties.getRoot()+path);
        List<String> fileList = new ArrayList<>();
        for(ChannelSftp.LsEntry entry: entries){
            String fileName = entry.getFilename();
            if(!FILE_TYPE_NAVIGATE_HOME_DIR.equals(fileName)&&!FILE_TYPE_NAVIGATE_PRE_DIR.equals(fileName)){
                fileList.add(fileName);
            }
        }
        return fileList;
    }
}
